CloudFormationで「Fn::Sub syntax must contain only alphanumeric characters, underscores, periods, and colons」と出た時の対処方法
CloudFormationでリソースを作成しようとしたときに「Fn::Sub syntax must contain only alphanumeric characters, underscores, periods, and colons」というエラーが出たのでその時の対処方法をブログに残します。
使用したCloudFormationテンプレート
以下のCloudFormationテンプレートはEC2を作成するものです。
さらにEC2の作成時にシェルスクリプトを実行するようにUserDataを設定してます。
UserDataではLinuxのユーザーを作成してEC2のメタデータからパブリックIPアドレスを取得して文字数をカウントしています。
AWSTemplateFormatVersion: "2010-09-09" Description: ec2 Stack Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# VolumeSize: Default: 8 Type: Number Ec2InstanceType: Default: t2.micro Type: String Vpcid: Type: AWS::EC2::VPC::Id Description: Enter VPC ID PublicSubnet1: Type: AWS::EC2::Subnet::Id Description: Enter Subnet ID LinuxUser: Default: kobayashi Type: String Resources: # ------------------------------------------------------------# # IAM # ------------------------------------------------------------# Ec2SsmRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore RoleName: EC2SsmRole Ec2IamInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: Ec2InstanceProfile Roles: - !Ref Ec2SsmRole # ------------------------------------------------------------# # Security Group # ------------------------------------------------------------# Ec2Sg: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for EC2 GroupName: ec2-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 Tags: - Key: Name Value: ec2-sg VpcId: !Ref Vpcid # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# Ec2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: !Ref VolumeSize VolumeType: gp3 IamInstanceProfile: !Ref Ec2IamInstanceProfile ImageId: ami-03dceaabddff8067e InstanceType: !Ref Ec2InstanceType NetworkInterfaces: - AssociatePublicIpAddress: true DeleteOnTermination: true DeviceIndex: 0 GroupSet: - !Ref Ec2Sg SubnetId: !Ref PublicSubnet1 Tags: - Key: Name Value: ec2 UserData: Fn::Base64: !Sub |- #!/bin/bash useradd ${LinuxUser} if [ -d /home/${LinuxUser} ]; then TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` && curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4 > /home/${LinuxUser}/meta_data.txt public_ip=`cat /home/${LinuxUser}/meta_data.txt` fi echo ${#public_ip} > /home/${LinuxUser}/word_count
このCloudFormationテンプレートを以下のAWS CLIコマンドでデプロイしてみます。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=Vpcid,ParameterValue=VPCのID ParameterKey=PublicSubnet1,ParameterValue=パブリックサブネットのID --capabilities CAPABILITY_NAMED_IAM
コマンドを実行すると以下のエラーが発生します。
An error occurred (ValidationError) when calling the CreateStack operation: Template error: variable names in Fn::Sub syntax must contain only alphanumeric characters, underscores, periods, and colons
原因と解決方法
原因はUserDataで設定したシェルスクリプト内にある「${#public_ip}」です。
UserDataではLinuxユーザーの名前をCloudFormationのパラメーターから取ってくるようにするため「Fn::Sub」を使用しています。
そのため、「${#public_ip}」もCloudFormationの変数として認識されてしまっています。
エラーの内容の通り、英数字、アンダースコアしか使用できないためエラーになっています。
解決方法は「Fn::Sub」のドキュメントに記載されていました。
USD 記号と中括弧 (${}) をそのまま書き込むには、最初の中括弧の後に感嘆符 (!) を追加します (${!Literal} など)。CloudFormation は、このテキストを ${Literal} として解決します。
つまり、感嘆符「!」を使用してエスケープする必要があります。
なのでUserDataの「${#public_ip}」を「${!#public_ip}」にすることで解決します。
エラーを解決したCloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: ec2 Stack Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# VolumeSize: Default: 8 Type: Number Ec2InstanceType: Default: t2.micro Type: String Vpcid: Type: AWS::EC2::VPC::Id Description: Enter VPC ID PublicSubnet1: Type: AWS::EC2::Subnet::Id Description: Enter Subnet ID LinuxUser: Default: kobayashi Type: String Resources: # ------------------------------------------------------------# # IAM # ------------------------------------------------------------# Ec2SsmRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore RoleName: EC2SsmRole Ec2IamInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: Ec2InstanceProfile Roles: - !Ref Ec2SsmRole # ------------------------------------------------------------# # Security Group # ------------------------------------------------------------# Ec2Sg: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for EC2 GroupName: ec2-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 Tags: - Key: Name Value: ec2-sg VpcId: !Ref Vpcid # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# Ec2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: !Ref VolumeSize VolumeType: gp3 IamInstanceProfile: !Ref Ec2IamInstanceProfile ImageId: ami-03dceaabddff8067e InstanceType: !Ref Ec2InstanceType NetworkInterfaces: - AssociatePublicIpAddress: true DeleteOnTermination: true DeviceIndex: 0 GroupSet: - !Ref Ec2Sg SubnetId: !Ref PublicSubnet1 Tags: - Key: Name Value: ec2 UserData: Fn::Base64: !Sub |- #!/bin/bash useradd ${LinuxUser} if [ -d /home/${LinuxUser} ]; then TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` && curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4 > /home/${LinuxUser}/meta_data.txt public_ip=`cat /home/${LinuxUser}/meta_data.txt` fi echo ${!#public_ip} > /home/${LinuxUser}/word_count
このCloudFormationテンプレートを以下のAWS CLIコマンドでデプロイするとエラーが出なくなっています。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=Vpcid,ParameterValue=VPCのID ParameterKey=PublicSubnet1,ParameterValue=パブリックサブネットのID --capabilities CAPABILITY_NAMED_IAM
さいごに
普段使用していてもまだまだ知らない仕様があるなといった感想です。
CloudFormationの関数は便利なものが多いのでしっかり仕様を把握して使いこなせるように今後も学習を続けていきます。